﻿using System;
using DoNet.Utils;

namespace DoNet.TenPay
{
    public class PayApiV2
    {
        /// <summary>
        /// 签名类型
        /// </summary>
        private static string sign_type = SignatureV2.SIGN_TYPE_HMAC_SHA256;

        #region API接口方法
        /// <summary>
        /// 统一下单
        /// </summary>
        /// <param name="trade_type">交易类型</param>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="total_fee">总金额</param>
        /// <param name="description">商品描述</param>
        /// <param name="notify_url">支付结果 通知地址</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="openid">用户标识</param>
        /// <param name="sub_openid">用户子标识</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="product_id">商品ID</param>
        /// <param name="attach">附加数据</param>
        /// <param name="time_start">交易起始时间</param>
        /// <param name="time_expire">交易结束时间</param>
        /// <param name="goods_tag">订单优惠标记</param>
        /// <param name="scene_info">场景信息</param>
        /// <param name="device_info">设备号 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"</param>
        /// <param name="detail">商品详情 商品详细描述，对于使用单品优惠的商户，该字段必须按照规范上传，详见“单品优惠参数说明”
        /// [{
        ///     "goods_detail":[
        ///     {
        ///         "goods_id":"iphone6s_16G",
        ///         "wxpay_goods_id":"1001",
        ///         "goods_name":"iPhone6s 16G",
        ///         "quantity":1,
        ///         "price":528800,
        ///         "goods_category":"123456",
        ///         "body":"苹果手机"
        ///     },
        ///     {
        ///         "goods_id":"iphone6s_32G",
        ///         "wxpay_goods_id":"1002",
        ///         "goods_name":"iPhone6s 32G",
        ///         "quantity":1,
        ///         "price":608800,
        ///         "goods_category":"123789",
        ///         "body":"苹果手机"
        ///     }
        ///     ]
        /// }]
        /// </param>
        /// <param name="fee_type">货币类型 符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型</param>
        /// <param name="limit_pay">指定支付方式  no_credit--指定不能使用信用卡支付</param>
        /// <param name="receipt">电子发票入口开放标识    Y，传入Y时，支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能，传此字段才可生效</param>
        /// <param name="profit_sharing">是否需要分账 Y-是，需要分账 N-否，不分账 字母要求大写，不传默认不分账</param>
        /// <param name="auth_code">付款码 扫码支付付款码，设备读取用户微信中的条码或者二维码信息（注：用户付款码条形码规则：18位纯数字，以10、11、12、13、14、15开头）</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns></returns>
        public static string UnifiedOrder(string trade_type, string appid, string mchid, string apiKeyV2, string out_trade_no, string total_fee, string description, string notify_url, int payMode = 0, string openid = "", string sub_openid = "", string sub_appid = "", string sub_mchid = "", string product_id = "", string attach = "", string time_start = "", string time_expire = "", string goods_tag = "", object scene_info = null, string device_info = "", object detail = null, string fee_type = "CNY", string limit_pay = "", string receipt = "", string profit_sharing = "N", string auth_code = "", int timeOut = 6)
        {
            string result = null;

            //请求参数
            RequestBase requestBase = new RequestBase();

            string url = DomainUtil.GetDomainName(Config.GetWechatPayUrl(), "/pay/unifiedorder");

            //检测必填参数
            if (!string.IsNullOrEmpty(out_trade_no))
            {
                if (!string.IsNullOrEmpty(description))
                {
                    if (!string.IsNullOrEmpty(total_fee))
                    {
                        if (!string.IsNullOrEmpty(trade_type))
                        {
                            if (trade_type != "JSAPI" || (trade_type == "JSAPI" && (!string.IsNullOrEmpty(openid) || !string.IsNullOrEmpty(sub_openid))))
                            {
                                if (trade_type != "NATIVE" || (trade_type == "NATIVE" && !string.IsNullOrEmpty(product_id)))
                                {
                                    if (trade_type != "MWEB" || (trade_type == "MWEB" && scene_info != null))
                                    {
                                        //付款码
                                        if (trade_type != "MICROPAY" || (trade_type == "MICROPAY" && !string.IsNullOrEmpty(auth_code)))
                                        {
                                            requestBase.setParameter("appid", appid);//服务商商户的APPID(公众号APPID)
                                            requestBase.setParameter("mch_id", mchid);//商户号
                                            requestBase.setParameter("body", description);//商户号
                                            if (!string.IsNullOrEmpty(attach))
                                            {
                                                requestBase.setParameter("attach", attach);
                                            }
                                            requestBase.setParameter("out_trade_no", out_trade_no);
                                            requestBase.setParameter("total_fee", Convert.ToInt32(Convert.ToDecimal(total_fee) * 100).ToString());

                                            if (trade_type != "MICROPAY")
                                            {
                                                requestBase.setParameter("notify_url", notify_url);//异步通知url
                                                requestBase.setParameter("trade_type", trade_type);

                                                //trade_type=NATIVE时，product_id 此参数必传
                                                if (!string.IsNullOrEmpty(product_id))
                                                {
                                                    requestBase.setParameter("product_id", product_id);
                                                }
                                            }
                                            else
                                            {
                                                //trade_type=MICROPAY auth_code 此参数必传
                                                if (!string.IsNullOrEmpty(auth_code))
                                                {
                                                    requestBase.setParameter("auth_code", auth_code);
                                                }
                                            }

                                            if (payMode == 1)
                                            {
                                                if (!string.IsNullOrEmpty(sub_mchid))
                                                {
                                                    requestBase.setParameter("sub_mch_id", sub_mchid);//子商户号
                                                }

                                                if (trade_type != "MICROPAY")
                                                {
                                                    //如果选择传sub_openid,则必须传sub_appid
                                                    if (!string.IsNullOrEmpty(sub_openid))
                                                    {
                                                        requestBase.setParameter("sub_openid", sub_openid);//小程序的openid
                                                    }
                                                }

                                                #region 子商户关联APPID
                                                if (!string.IsNullOrEmpty(sub_appid))
                                                {
                                                    requestBase.setParameter("sub_appid", sub_appid);//子商户关联APPID
                                                }
                                                #endregion
                                            }

                                            if (trade_type != "MICROPAY")
                                            {
                                                if (!string.IsNullOrEmpty(openid))
                                                {
                                                    requestBase.setParameter("openid", openid);
                                                }
                                            }

                                            requestBase.setParameter("spbill_create_ip", IPUtil.GetAddressIP());//终端ip
                                            requestBase.setParameter("nonce_str", StringUtil.GenerateNonceStr());//随机字符串

                                            if (!string.IsNullOrEmpty(time_start))
                                            {
                                                requestBase.setParameter("time_start", time_start);
                                            }
                                            if (!string.IsNullOrEmpty(time_expire))
                                            {
                                                requestBase.setParameter("time_expire", time_expire);
                                            }
                                            if (!string.IsNullOrEmpty(goods_tag))
                                            {
                                                requestBase.setParameter("goods_tag", goods_tag);
                                            }
                                            //场景信息
                                            if (scene_info != null)
                                            {
                                                requestBase.setParameter("scene_info", scene_info);
                                            }
                                            if (!string.IsNullOrEmpty(device_info))
                                            {
                                                requestBase.setParameter("device_info", device_info);
                                            }
                                            if (detail != null)
                                            {
                                                requestBase.setParameter("detail", detail);
                                            }
                                            if (!string.IsNullOrEmpty(fee_type))
                                            {
                                                requestBase.setParameter("fee_type", fee_type);
                                            }
                                            if (!string.IsNullOrEmpty(limit_pay))
                                            {
                                                requestBase.setParameter("limit_pay", limit_pay);
                                            }
                                            if (!string.IsNullOrEmpty(receipt))
                                            {
                                                requestBase.setParameter("receipt", receipt);
                                            }
                                            if (!string.IsNullOrEmpty(profit_sharing))
                                            {
                                                requestBase.setParameter("profit_sharing", profit_sharing);
                                            }

                                            requestBase.setParameter("sign_type", sign_type);//签名类型


                                            var hashtable = requestBase.getAllParameters();
                                            string paySign = SignatureV2.MakeSign(hashtable, sign_type, apiKeyV2);
                                            requestBase.setParameter("sign", paySign); //签名

                                            string xml = SignatureV2.ToXml(requestBase.getAllParameters());

                                            var start = DateTime.Now;
                                            Log.Debug("Wechat—V2—PayApiV2—UnifiedOrder", "UnfiedOrder request : " + xml);
                                            ResponseHandlerV2 responseHandler = new ResponseHandlerV2(url, mchid, "POST", xml, false, null, null, timeOut);
                                            var end = DateTime.Now;
                                            TimeSpan timespan = end.Subtract(start);//时间间隔
                                            Log.Debug("Wechat—V2—PayApiV2—UnifiedOrder", "UnfiedOrder 耗时 : " + timespan + " 秒");

                                            string responseBody = responseHandler.ResponseBody;
                                            Log.Debug("Wechat—V2—PayApiV2—UnifiedOrder", "UnfiedOrder response : " + responseHandler.ResponseBody);
                                            if (!string.IsNullOrEmpty(responseBody))
                                            {
                                                var orderData = JsonUtil.JsonToT<ResponseUnifiedOrderBase>(responseBody);
                                                string return_code = orderData.return_code;
                                                if (return_code == "SUCCESS")
                                                {
                                                    string result_code = orderData.result_code;
                                                    if (result_code == "SUCCESS")
                                                    {
                                                        result = responseBody;
                                                    }
                                                    else
                                                    {
                                                        Log.Error("Wechat—V2—PayApiV2—UnifiedOrder", "返回结果result_code【" + result_code + "】！");
                                                    }
                                                }
                                                else
                                                {
                                                    Log.Error("Wechat—V2—PayApiV2—UnifiedOrder", "返回结果return_code【" + return_code + "】！");
                                                }
                                            }
                                        }
                                        else
                                        {
                                            Log.Error("Wechat—V2—PayApiV2—UnifiedOrder", "统一支付接口中，trade_type为MICROPAY时，auth_code为必填参数！");
                                        }
                                    }
                                    else
                                    {
                                        Log.Error("Wechat—V2—PayApiV2—UnifiedOrder", "统一支付接口中，trade_type为MWEB时，scene_info为必填参数！");
                                    }
                                }
                                else
                                {
                                    Log.Error("Wechat—V2—PayApiV2—UnifiedOrder", "统一支付接口中，trade_type为NATIVE时，product_id为必填参数！");
                                }
                            }
                            else
                            {
                                Log.Error("Wechat—V2—PayApiV2—UnifiedOrder", "统一支付接口中，trade_type为JSAPI时，openid或sub_openid为必填参数！");
                            }
                        }
                        else
                        {
                            Log.Error("Wechat—V2—PayApiV2—UnifiedOrder", "缺少统一支付接口必填参数trade_type！");
                        }
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—UnifiedOrder", "缺少统一支付接口必填参数total_fee！");
                    }
                }
                else
                {
                    Log.Error("Wechat—V2—PayApiV2—UnifiedOrder", "缺少统一支付接口必填参数body！");
                }
            }
            else
            {
                Log.Error("Wechat—V2—PayApiV2—UnifiedOrder", "缺少统一支付接口必填参数out_trade_no！");
            }
            return result;
        }

        /// <summary>
        /// JSAPI下单
        /// 除付款码支付场景以外，商户系统先调用该接口在微信支付服务后台生成预支付交易单，返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。
        /// </summary>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="total_fee">总金额</param>
        /// <param name="description">商品描述</param>
        /// <param name="notify_url">支付结果 通知地址</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="openid">用户标识</param>
        /// <param name="sub_openid">用户子标识</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="product_id">商品ID</param>
        /// <param name="attach">附加数据</param>
        /// <param name="time_start">交易起始时间</param>
        /// <param name="time_expire">交易结束时间</param>
        /// <param name="goods_tag">订单优惠标记</param>
        /// <param name="scene_info">场景信息</param>
        /// <param name="device_info">设备号 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"</param>
        /// <param name="detail">商品详情 商品详细描述，对于使用单品优惠的商户，该字段必须按照规范上传，详见“单品优惠参数说明”
        /// [{
        ///     "goods_detail":[
        ///     {
        ///         "goods_id":"iphone6s_16G",
        ///         "wxpay_goods_id":"1001",
        ///         "goods_name":"iPhone6s 16G",
        ///         "quantity":1,
        ///         "price":528800,
        ///         "goods_category":"123456",
        ///         "body":"苹果手机"
        ///     },
        ///     {
        ///         "goods_id":"iphone6s_32G",
        ///         "wxpay_goods_id":"1002",
        ///         "goods_name":"iPhone6s 32G",
        ///         "quantity":1,
        ///         "price":608800,
        ///         "goods_category":"123789",
        ///         "body":"苹果手机"
        ///     }
        ///     ]
        /// }]
        /// </param>
        /// <param name="fee_type">货币类型 符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型</param>
        /// <param name="limit_pay">指定支付方式  no_credit--指定不能使用信用卡支付</param>
        /// <param name="receipt">电子发票入口开放标识    Y，传入Y时，支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能，传此字段才可生效</param>
        /// <param name="profit_sharing">是否需要分账 Y-是，需要分账 N-否，不分账 字母要求大写，不传默认不分账</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns></returns>
        public static ResponseUnifiedOrderJsApiResult JsApiPay(string appid, string mchid, string apiKeyV2, string out_trade_no, string total_fee, string description, string notify_url, int payMode = 0, string openid = "", string sub_openid = "", string sub_appid = "", string sub_mchid = "", string product_id = "", string attach = "", string time_start = "", string time_expire = "", string goods_tag = "", object scene_info = null, string device_info = "", object detail = null, string fee_type = "CNY", string limit_pay = "", string receipt = "", string profit_sharing = "N", int timeOut = 6)
        {
            ResponseUnifiedOrderJsApiResult result = null;
            string orderDataStr = UnifiedOrder("JSAPI", appid, mchid, apiKeyV2, out_trade_no, total_fee, description, notify_url, payMode, openid, sub_openid, sub_mchid, sub_appid, product_id, attach, time_start, time_expire, goods_tag, scene_info, device_info, detail, fee_type, limit_pay, receipt, profit_sharing, "", timeOut);
            if (!string.IsNullOrEmpty(orderDataStr))
            {
                var orderData = JsonUtil.JsonToT<ResponseUnifiedOrderJsApiResult>(orderDataStr);
                if (orderData != null)
                {
                    var hashtable = SignatureV2.ObjectToHashtable(orderData);
                    //验证签名
                    if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                    {
                        result = orderData;
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—JsApiPay", "签名验证错误！");
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// APP下单API
        /// 服务商系统先调用该接口在微信支付服务后台生成预支付交易单，返回正确的预支付交易会话标识后再返回给子商户在APP里面调起支付。
        /// </summary>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="total_fee">总金额</param>
        /// <param name="description">商品描述</param>
        /// <param name="notify_url">支付结果 通知地址</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="attach">附加数据</param>
        /// <param name="time_start">交易起始时间</param>
        /// <param name="time_expire">交易结束时间</param>
        /// <param name="goods_tag">订单优惠标记</param>
        /// <param name="scene_info">场景信息</param>
        /// <param name="device_info">设备号 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"</param>
        /// <param name="detail">商品详情 商品详细描述，对于使用单品优惠的商户，该字段必须按照规范上传，详见“单品优惠参数说明”
        /// [{
        ///     "goods_detail":[
        ///     {
        ///         "goods_id":"iphone6s_16G",
        ///         "wxpay_goods_id":"1001",
        ///         "goods_name":"iPhone6s 16G",
        ///         "quantity":1,
        ///         "price":528800,
        ///         "goods_category":"123456",
        ///         "body":"苹果手机"
        ///     },
        ///     {
        ///         "goods_id":"iphone6s_32G",
        ///         "wxpay_goods_id":"1002",
        ///         "goods_name":"iPhone6s 32G",
        ///         "quantity":1,
        ///         "price":608800,
        ///         "goods_category":"123789",
        ///         "body":"苹果手机"
        ///     }
        ///     ]
        /// }]
        /// </param>
        /// <param name="fee_type">货币类型 符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型</param>
        /// <param name="limit_pay">指定支付方式  no_credit--指定不能使用信用卡支付</param>
        /// <param name="receipt">电子发票入口开放标识    Y，传入Y时，支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能，传此字段才可生效</param>
        /// <param name="profit_sharing">是否需要分账 Y-是，需要分账 N-否，不分账 字母要求大写，不传默认不分账</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns></returns>
        public static ResponseUnifiedOrderAppResult AppPay(string appid, string mchid, string apiKeyV2, string out_trade_no, string total_fee, string description, string notify_url, int payMode = 0, string sub_appid = "", string sub_mchid = "", string attach = "", string time_start = "", string time_expire = "", string goods_tag = "", object scene_info = null, string device_info = "", object detail = null, string fee_type = "CNY", string limit_pay = "", string receipt = "", string profit_sharing = "N", int timeOut = 6)
        {
            ResponseUnifiedOrderAppResult result = null;
            string orderDataStr = UnifiedOrder("APP", appid, mchid, apiKeyV2, out_trade_no, total_fee, description, notify_url, payMode, "", "", sub_appid, sub_mchid, "", attach, time_start, time_expire, goods_tag, scene_info, device_info, detail, fee_type, limit_pay, receipt, profit_sharing, "", timeOut);
            if (!string.IsNullOrEmpty(orderDataStr))
            {
                var orderData = JsonUtil.JsonToT<ResponseUnifiedOrderAppResult>(orderDataStr);
                if (orderData != null)
                {
                    var hashtable = SignatureV2.ObjectToHashtable(orderData);
                    //验证签名
                    if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                    {
                        result = orderData;
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—AppPay", "签名验证错误！");
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// H5下单
        /// 商户系统先调用该接口在微信支付服务后台生成预支付交易单，返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。
        /// 注意：服务商和商户的url地址不一样
        /// </summary>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="total_fee">总金额</param>
        /// <param name="description">商品描述</param>
        /// <param name="notify_url">支付结果 通知地址</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="openid">用户标识</param>
        /// <param name="sub_openid">用户子标识</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="product_id">商品ID</param>
        /// <param name="attach">附加数据</param>
        /// <param name="time_start">交易起始时间</param>
        /// <param name="time_expire">交易结束时间</param>
        /// <param name="goods_tag">订单优惠标记</param>
        /// <param name="scene_info">场景信息</param>
        /// <param name="device_info">设备号 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"</param>
        /// <param name="detail">商品详情 商品详细描述，对于使用单品优惠的商户，该字段必须按照规范上传，详见“单品优惠参数说明”
        /// [{
        ///     "goods_detail":[
        ///     {
        ///         "goods_id":"iphone6s_16G",
        ///         "wxpay_goods_id":"1001",
        ///         "goods_name":"iPhone6s 16G",
        ///         "quantity":1,
        ///         "price":528800,
        ///         "goods_category":"123456",
        ///         "body":"苹果手机"
        ///     },
        ///     {
        ///         "goods_id":"iphone6s_32G",
        ///         "wxpay_goods_id":"1002",
        ///         "goods_name":"iPhone6s 32G",
        ///         "quantity":1,
        ///         "price":608800,
        ///         "goods_category":"123789",
        ///         "body":"苹果手机"
        ///     }
        ///     ]
        /// }]
        /// </param>
        /// <param name="fee_type">货币类型 符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型</param>
        /// <param name="limit_pay">指定支付方式  no_credit--指定不能使用信用卡支付</param>
        /// <param name="receipt">电子发票入口开放标识    Y，传入Y时，支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能，传此字段才可生效</param>
        /// <param name="profit_sharing">是否需要分账 Y-是，需要分账 N-否，不分账 字母要求大写，不传默认不分账</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns></returns>
        public static ResponseUnifiedOrderH5Result H5Pay(string appid, string mchid, string apiKeyV2, string out_trade_no, string total_fee, string description, string notify_url, object scene_info, int payMode = 0, string openid = "", string sub_openid = "", string sub_appid = "", string sub_mchid = "", string product_id = "", string attach = "", string time_start = "", string time_expire = "", string goods_tag = "", string device_info = "", object detail = null, string fee_type = "CNY", string limit_pay = "", string receipt = "", string profit_sharing = "N", int timeOut = 6)
        {
            ResponseUnifiedOrderH5Result result = null;
            string orderDataStr = UnifiedOrder("MWEB", appid, mchid, apiKeyV2, out_trade_no, total_fee, description, notify_url, payMode, openid, sub_openid, sub_appid, sub_mchid, product_id, attach, time_start, time_expire, goods_tag, scene_info, device_info, detail, fee_type, limit_pay, receipt, profit_sharing, "", timeOut);
            if (!string.IsNullOrEmpty(orderDataStr))
            {
                var orderData = JsonUtil.JsonToT<ResponseUnifiedOrderH5Result>(orderDataStr);
                if (orderData != null)
                {
                    var hashtable = SignatureV2.ObjectToHashtable(orderData);
                    //验证签名
                    if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                    {
                        result = orderData;
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—H5Pay", "签名验证错误！");
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Native下单API
        /// 商户Native支付下单接口，微信后台系统返回链接参数code_url，商户后台系统将code_url值生成二维码图片，用户使用微信客户端扫码后发起支付。
        /// 注意：服务商和商户的url地址不一样
        /// </summary>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="total_fee">总金额</param>
        /// <param name="description">商品描述</param>
        /// <param name="notify_url">支付结果 通知地址</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="openid">用户标识</param>
        /// <param name="sub_openid">用户子标识</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="product_id">商品ID</param>
        /// <param name="attach">附加数据</param>
        /// <param name="time_start">交易起始时间</param>
        /// <param name="time_expire">交易结束时间</param>
        /// <param name="goods_tag">订单优惠标记</param>
        /// <param name="scene_info">场景信息</param>
        /// <param name="device_info">设备号 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"</param>
        /// <param name="detail">商品详情 商品详细描述，对于使用单品优惠的商户，该字段必须按照规范上传，详见“单品优惠参数说明”
        /// [{
        ///     "goods_detail":[
        ///     {
        ///         "goods_id":"iphone6s_16G",
        ///         "wxpay_goods_id":"1001",
        ///         "goods_name":"iPhone6s 16G",
        ///         "quantity":1,
        ///         "price":528800,
        ///         "goods_category":"123456",
        ///         "body":"苹果手机"
        ///     },
        ///     {
        ///         "goods_id":"iphone6s_32G",
        ///         "wxpay_goods_id":"1002",
        ///         "goods_name":"iPhone6s 32G",
        ///         "quantity":1,
        ///         "price":608800,
        ///         "goods_category":"123789",
        ///         "body":"苹果手机"
        ///     }
        ///     ]
        /// }]
        /// </param>
        /// <param name="fee_type">货币类型 符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型</param>
        /// <param name="limit_pay">指定支付方式  no_credit--指定不能使用信用卡支付</param>
        /// <param name="receipt">电子发票入口开放标识    Y，传入Y时，支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能，传此字段才可生效</param>
        /// <param name="profit_sharing">是否需要分账 Y-是，需要分账 N-否，不分账 字母要求大写，不传默认不分账</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns></returns>
        public static ResponseUnifiedOrderNativeResult NativePay(string appid, string mchid, string apiKeyV2, string out_trade_no, string total_fee, string description, string notify_url, int payMode = 0, string openid = "", string sub_openid = "", string sub_appid = "", string sub_mchid = "", string product_id = "", string attach = "", string time_start = "", string time_expire = "", string goods_tag = "", object scene_info = null, string device_info = "", object detail = null, string fee_type = "CNY", string limit_pay = "", string receipt = "", string profit_sharing = "N", int timeOut = 6)
        {
            ResponseUnifiedOrderNativeResult result = null;
            string orderDataStr = UnifiedOrder("NATIVE", appid, mchid, apiKeyV2, out_trade_no, total_fee, description, notify_url, payMode, openid, sub_openid, sub_appid, sub_mchid, product_id, attach, time_start, time_expire, goods_tag, scene_info, device_info, detail, fee_type, limit_pay, receipt, profit_sharing, "", timeOut);
            if (!string.IsNullOrEmpty(orderDataStr))
            {
                var orderData = JsonUtil.JsonToT<ResponseUnifiedOrderNativeResult>(orderDataStr);
                if (orderData != null)
                {
                    var hashtable = SignatureV2.ObjectToHashtable(orderData);
                    //验证签名
                    if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                    {
                        result = orderData;
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—NativePay", "签名验证错误！");
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 付款码支付
        /// 提交被扫支付API
        /// 收银员使用扫码设备读取微信用户刷卡授权码以后，二维码或条码信息传送至商户收银台，
        /// 由商户收银台或者商户后台调用该接口发起支付。
        /// </summary>
        /// <param name="appid">(服务商)商户的APPID</param>
        /// <param name="mchid">(服务商)商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="description">商品描述 商品简单描述，该字段须严格按照规范传递，具体请见参数规定</param>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="total_fee">总金额</param>
        /// <param name="auth_code">付款码</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="attach">附加数据</param>
        /// <param name="time_start">交易起始时间</param>
        /// <param name="time_expire">交易结束时间</param>
        /// <param name="goods_tag">订单优惠标记</param>
        /// <param name="scene_info">订单优惠标记</param>
        /// <param name="device_info">设备号 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"</param>
        /// <param name="detail">商品详情 商品详细描述，对于使用单品优惠的商户，该字段必须按照规范上传，详见“单品优惠参数说明”
        /// [{
        ///     "goods_detail":[
        ///     {
        ///         "goods_id":"iphone6s_16G",
        ///         "wxpay_goods_id":"1001",
        ///         "goods_name":"iPhone6s 16G",
        ///         "quantity":1,
        ///         "price":528800,
        ///         "goods_category":"123456",
        ///         "body":"苹果手机"
        ///     },
        ///     {
        ///         "goods_id":"iphone6s_32G",
        ///         "wxpay_goods_id":"1002",
        ///         "goods_name":"iPhone6s 32G",
        ///         "quantity":1,
        ///         "price":608800,
        ///         "goods_category":"123789",
        ///         "body":"苹果手机"
        ///     }
        ///     ]
        /// }]
        /// <param name="fee_type">货币类型 符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型</param>
        /// <param name="limit_pay">指定支付方式  no_credit--指定不能使用信用卡支付</param>
        /// <param name="receipt">电子发票入口开放标识    Y，传入Y时，支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能，传此字段才可生效</param>
        /// <param name="profit_sharing">是否需要分账 Y-是，需要分账 N-否，不分账 字母要求大写，不传默认不分账</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns>
        /*
        用户标识			    openid	是	String(128)	用户在商户appid 下的唯一标识
        是否关注公众账号	    is_subscribe	是	String(1)	用户是否关注公众账号，仅在公众账号类型支付有效，取值范围：Y或N;Y-关注;N-未关注
        用户子标识			    sub_openid	否	String(128)	子商户appid下用户唯一标识，如需返回则请求时需要传sub_appid
        是否关注子公众账号	    sub_is_subscribe	否	String(1)	用户是否关注子公众账号，仅在公众账号类型支付有效，取值范围：Y或N;Y-关注;N-未关注
        付款银行			    bank_type	是	String(32)	银行类型，采用字符串类型的银行标识，值列表详见银行类型
        标价币种			    fee_type	否	String(8)	符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型
        标价金额			    total_fee	是	int	订单总金额，单位为分，只能为整数，详见支付金额
        现金支付币种		    cash_fee_type	否	String(16)	符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型
        现金支付金额		    cash_fee	是	int	订单现金支付金额，详见支付金额
        应结订单金额		    settlement_total_fee	否	int	当订单使用了免充值型优惠券后返回该参数，应结订单金额=订单金额-免充值优惠券金额。
        代金券金额			    coupon_fee	否	int	“代金券”金额<=订单金额，订单金额-“代金券”金额=现金支付金额，详见支付金额
        微信支付订单号		    transaction_id	是	String(32)	微信支付订单号
        商户订单号			    out_trade_no	是	String(32)	商户系统内部订单号，要求32个字符内（最少6个字符），只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见商户订单号
        商家数据包			    attach	否	String(128)	商家数据包，原样返回
        支付完成时间		    time_end	是	String(14)	订单生成时间，格式为yyyyMMddHHmmss，如2009年12月25日9点10分10秒表示为20091225091010。详见时间规则
        */
        /// </returns>
        public static ResponseUnifiedOrderMicroPayResult MicroPay(string appid, string mchid, string apiKeyV2, string description, string out_trade_no, string total_fee, string auth_code, int payMode = 0, string sub_appid = "", string sub_mchid = "", string attach = "", string time_start = "", string time_expire = "", string goods_tag = "", object scene_info = null, string device_info = "", object detail = null, string fee_type = "CNY", string limit_pay = "", string receipt = "", string profit_sharing = "N", int timeOut = 10)
        {
            ResponseUnifiedOrderMicroPayResult result = null;
            string orderDataStr = UnifiedOrder("MICROPAY", appid, mchid, apiKeyV2, out_trade_no, total_fee, description, "", payMode, "", "", sub_appid, sub_mchid, "", attach, time_start, time_expire, goods_tag, scene_info, device_info, detail, fee_type, limit_pay, receipt, profit_sharing, auth_code, timeOut);
            if (!string.IsNullOrEmpty(orderDataStr))
            {
                var orderData = JsonUtil.JsonToT<ResponseUnifiedOrderMicroPayResult>(orderDataStr);
                if (orderData != null)
                {
                    var hashtable = SignatureV2.ObjectToHashtable(orderData);
                    //验证签名
                    if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                    {
                        result = orderData;
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—MicroPay", "签名验证错误！");
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 查询订单
        /// 商户订单号/微信订单号 二选一
        /// </summary>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="transaction_id">微信订单号</param>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns></returns>
        public static ResponseOrderQueryResult OrderQuery(string out_trade_no, string transaction_id, string appid, string mchid, string apiKeyV2, int payMode = 0, string sub_appid = "", string sub_mchid = "", int timeOut = 6)
        {
            ResponseOrderQueryResult result = null;

            //请求参数
            RequestBase requestBase = new RequestBase();

            string url = DomainUtil.GetDomainName(Config.GetWechatPayUrl(), "/pay/orderquery");

            //检测必填参数
            if (!string.IsNullOrEmpty(out_trade_no) || !string.IsNullOrEmpty(transaction_id))
            {
                if (!string.IsNullOrEmpty(out_trade_no))
                {
                    requestBase.setParameter("out_trade_no", out_trade_no);
                }
                if (!string.IsNullOrEmpty(transaction_id))
                {
                    requestBase.setParameter("transaction_id", transaction_id);
                }
                requestBase.setParameter("appid", appid);//服务商的APPID
                requestBase.setParameter("mch_id", mchid);//商户号

                if (payMode == 1)
                {
                    #region 子商户关联APPID
                    if (!string.IsNullOrEmpty(sub_appid))
                    {
                        requestBase.setParameter("sub_appid", sub_appid);//子商户关联APPID
                    }
                    #endregion
                    #region 子商户号
                    if (!string.IsNullOrEmpty(sub_mchid))
                    {
                        requestBase.setParameter("sub_mch_id", sub_mchid);//子商户号
                    }
                    #endregion
                }

                requestBase.setParameter("nonce_str", StringUtil.GenerateNonceStr());//随机字符串
                requestBase.setParameter("sign_type", sign_type);//签名类型
                //签名
                string sign = SignatureV2.MakeSign(requestBase.getAllParameters(), sign_type, apiKeyV2);
                requestBase.setParameter("sign", sign);

                string xml = SignatureV2.ToXml(requestBase.getAllParameters());

                var start = DateTime.Now;
                Log.Debug("Wechat—V2—PayApiV2—OrderQuery", "OrderQuery request : " + xml);
                ResponseHandlerV2 responseHandler = new ResponseHandlerV2(url, mchid, "POST", xml, false, null, null, timeOut);
                var end = DateTime.Now;
                TimeSpan timespan = end.Subtract(start);//时间间隔
                Log.Debug("Wechat—V2—PayApiV2—OrderQuery", "OrderQuery 耗时 : " + timespan + " 秒");

                string responseBody = responseHandler.ResponseBody;
                Log.Debug("Wechat—V2—PayApiV2—OrderQuery", "OrderQuery response : " + responseBody);
                if (!string.IsNullOrEmpty(responseBody))
                {
                    var orderData = JsonUtil.JsonToT<ResponseOrderQueryResult>(responseBody);
                    string return_code = orderData.return_code;
                    if (return_code == "SUCCESS")
                    {
                        string result_code = orderData.result_code;
                        if (result_code == "SUCCESS")
                        {
                            var hashtable = SignatureV2.ObjectToHashtable(orderData);
                            //验证签名
                            if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                            {
                                result = orderData;
                            }
                            else
                            {
                                Log.Error("Wechat—V2—PayApiV2—OrderQuery", "签名验证错误！");
                            }
                        }
                        else
                        {
                            Log.Error("Wechat—V2—PayApiV2—OrderQuery", "返回结果result_code【" + result_code + "】！");
                        }
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—OrderQuery", "返回结果return_code【" + return_code + "】！");
                    }
                }
                else
                {
                    Log.Error("Wechat—V2—PayApiV2—OrderQuery", "查询订单失败！");
                }
            }
            else
            {
                Log.Error("Wechat—V2—PayApiV2—OrderQuery", "订单查询接口中，out_trade_no、transaction_id至少填一个！");
            }
            return result;
        }

        /// <summary>
        /// 撤销订单API接口 (付款码支付专用)
        /// 提交给撤销订单API接口的参数，out_trade_no和transaction_id必填一个
        /// 支付交易返回失败或支付系统超时，调用该接口撤销交易。如果此订单用户支付失败，微信支付系统会将此订单关闭；如果用户支付成功，微信支付系统会将此订单资金退还给用户。
        /// 注意：7天以内的交易单可调用撤销，其他正常支付的单如需实现相同功能请调用申请退款API。提交支付交易后调用【查询订单API】，没有明确的支付结果再调用【撤销订单API】。
        /// 调用支付接口后请勿立即调用撤销订单API，建议支付后至少15s后再调用撤销订单接口。
        /// </summary>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="transaction_id">微信订单号</param>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns></returns>
        public static ResponseReverseResult Reverse(string out_trade_no, string transaction_id, string appid, string mchid, string apiKeyV2, int payMode = 0, string sub_appid = "", string sub_mchid = "", int timeOut = 6)
        {
            ResponseReverseResult result = null;

            //请求参数
            RequestBase requestBase = new RequestBase();

            string url = DomainUtil.GetDomainName(Config.GetWechatPayUrl(), "/secapi/pay/reverse");

            //检测必填参数
            if (!string.IsNullOrEmpty(out_trade_no) || !string.IsNullOrEmpty(transaction_id))
            {
                if (!string.IsNullOrEmpty(out_trade_no))
                {
                    requestBase.setParameter("out_trade_no", out_trade_no);
                }
                if (!string.IsNullOrEmpty(transaction_id))
                {
                    requestBase.setParameter("transaction_id", transaction_id);
                }

                requestBase.setParameter("appid", appid);//服务商的APPID
                requestBase.setParameter("mch_id", mchid);//商户号
                if (payMode == 1)
                {
                    #region 子商户号
                    if (!string.IsNullOrEmpty(sub_mchid))
                    {
                        requestBase.setParameter("sub_mch_id", sub_mchid);//子商户号
                    }
                    #endregion
                    #region 子商户关联APPID
                    if (!string.IsNullOrEmpty(sub_appid))
                    {
                        requestBase.setParameter("sub_appid", sub_appid);//子商户关联APPID
                    }
                    #endregion
                }

                requestBase.setParameter("nonce_str", StringUtil.GenerateNonceStr());//随机字符串
                requestBase.setParameter("sign_type", sign_type);//签名类型
                //签名
                string sign = SignatureV2.MakeSign(requestBase.getAllParameters(), sign_type, apiKeyV2);
                requestBase.setParameter("sign", sign);

                string xml = SignatureV2.ToXml(requestBase.getAllParameters());

                var start = DateTime.Now;
                Log.Debug("Wechat—V2—PayApiV2—Reverse", "Reverse request : " + xml);
                ResponseHandlerV2 responseHandler = new ResponseHandlerV2(url, mchid, "POST", xml, false, null, null, timeOut);
                var end = DateTime.Now;
                TimeSpan timespan = end.Subtract(start);//时间间隔
                Log.Debug("Wechat—V2—PayApiV2—Reverse", "Reverse 耗时 : " + timespan + " 秒");

                string responseBody = responseHandler.ResponseBody;
                Log.Debug("Wechat—V2—PayApiV2—Reverse", "Reverse response : " + responseBody);
                if (!string.IsNullOrEmpty(responseBody))
                {
                    var orderData = JsonUtil.JsonToT<ResponseReverseResult>(responseBody);
                    string return_code = orderData.return_code;
                    if (return_code == "SUCCESS")
                    {
                        string result_code = orderData.result_code;
                        if (result_code == "SUCCESS")
                        {
                            var hashtable = SignatureV2.ObjectToHashtable(orderData);
                            //验证签名
                            if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                            {
                                result = orderData;
                            }
                            else
                            {
                                Log.Error("Wechat—V2—PayApiV2—Reverse", "签名验证错误！");
                            }
                        }
                        else
                        {
                            Log.Error("Wechat—V2—PayApiV2—Reverse", "返回结果result_code【" + result_code + "】！");
                        }
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—Reverse", "返回结果return_code【" + return_code + "】！");
                    }
                }
                else
                {
                    Log.Error("Wechat—V2—PayApiV2—Reverse", "撤销订单API接口失败！");
                }
            }
            else
            {
                Log.Error("Wechat—V2—PayApiV2—Reverse", "撤销订单API接口中，参数out_trade_no和transaction_id必须填写一个！");
            }
            return result;
        }

        /// <summary>
        /// 申请退款
        /// 提交给撤销订单API接口的参数，out_trade_no和transaction_id必填一个
        /// </summary>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="transaction_id">微信订单号</param>
        /// <param name="out_refund_no">商户退款单号</param>
        /// <param name="total_fee">订单金额</param>
        /// <param name="refund_fee">申请退款金额</param>
        /// <param name="refund_desc">退款原因</param>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="notify_url">申请退款结果通知url</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns>成功时返回接口调用结果，其他抛异常</returns>
        public static ResponseRefundResult Refund(string out_trade_no, string transaction_id, string out_refund_no, string total_fee, string refund_fee, string appid, string mchid, string apiKeyV2, string notify_url, int payMode = 0, string sub_appid = "", string sub_mchid = "", string refund_desc = "", int timeOut = 6)
        {
            ResponseRefundResult result = null;

            //请求参数
            RequestBase requestBase = new RequestBase();

            string url = DomainUtil.GetDomainName(Config.GetWechatPayUrl(), "/secapi/pay/refund");

            //检测必填参数
            if (!string.IsNullOrEmpty(out_trade_no) || !string.IsNullOrEmpty(transaction_id))
            {
                if (!string.IsNullOrEmpty(out_trade_no))
                {
                    requestBase.setParameter("out_trade_no", out_trade_no);
                }
                if (!string.IsNullOrEmpty(transaction_id))
                {
                    requestBase.setParameter("transaction_id", transaction_id);
                }
                if (!string.IsNullOrEmpty(refund_desc))
                {
                    requestBase.setParameter("refund_desc", refund_desc);
                }

                if (!string.IsNullOrEmpty(out_refund_no))
                {
                    requestBase.setParameter("out_refund_no", out_refund_no);
                    if (!string.IsNullOrEmpty(total_fee))
                    {
                        requestBase.setParameter("total_fee", int.Parse(total_fee));
                        if (!string.IsNullOrEmpty(refund_fee))
                        {
                            requestBase.setParameter("refund_fee", int.Parse(refund_fee));
                            requestBase.setParameter("notify_url", notify_url);//异步通知url
                            requestBase.setParameter("appid", appid);//服务商的APPID
                            requestBase.setParameter("mch_id", mchid);//商户号

                            if (payMode == 1)
                            {
                                #region 子商户关联APPID
                                if (!string.IsNullOrEmpty(sub_appid))
                                {
                                    requestBase.setParameter("sub_appid", sub_appid);//子商户关联APPID
                                }
                                #endregion
                                #region 子商户号
                                if (!string.IsNullOrEmpty(sub_mchid))
                                {
                                    requestBase.setParameter("sub_mch_id", sub_mchid);//子商户号
                                }
                                #endregion
                            }

                            requestBase.setParameter("nonce_str", StringUtil.GenerateNonceStr());//随机字符串
                            requestBase.setParameter("sign_type", sign_type);//签名类型
                            //签名
                            string sign = SignatureV2.MakeSign(requestBase.getAllParameters(), sign_type, apiKeyV2);
                            requestBase.setParameter("sign", sign);

                            string xml = SignatureV2.ToXml(requestBase.getAllParameters());

                            var start = DateTime.Now;
                            Log.Debug("Wechat—V2—PayApiV2—Refund", "Refund request : " + xml);
                            ResponseHandlerV2 responseHandler = new ResponseHandlerV2(url, mchid, "POST", xml, true, null, null, timeOut);
                            var end = DateTime.Now;
                            TimeSpan timespan = end.Subtract(start);//时间间隔
                            Log.Debug("Wechat—V2—PayApiV2—Refund", "Refund 耗时 : " + timespan + " 秒");

                            string responseBody = responseHandler.ResponseBody;
                            Log.Debug("Wechat—V2—PayApiV2—Refund", "Refund response : " + responseBody);
                            if (!string.IsNullOrEmpty(responseBody))
                            {
                                var orderData = JsonUtil.JsonToT<ResponseRefundResult>(responseBody);
                                string return_code = orderData.return_code;
                                if (return_code == "SUCCESS")
                                {
                                    string result_code = orderData.result_code;
                                    if (result_code == "SUCCESS")
                                    {
                                        var hashtable = SignatureV2.ObjectToHashtable(orderData);
                                        //验证签名
                                        if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                                        {
                                            result = orderData;
                                        }
                                        else
                                        {
                                            Log.Error("Wechat—V2—PayApiV2—Refund", "签名验证错误！");
                                        }
                                    }
                                    else
                                    {
                                        Log.Error("Wechat—V2—PayApiV2—Refund", "返回结果result_code【" + result_code + "】！");
                                    }
                                }
                                else
                                {
                                    Log.Error("Wechat—V2—PayApiV2—Refund", "返回结果return_code【" + return_code + "】！");
                                }
                            }
                            else
                            {
                                Log.Error("Wechat—V2—PayApiV2—Refund", "申请退款失败！");
                            }
                        }
                        else
                        {
                            Log.Error("Wechat—V2—PayApiV2—Refund", "退款申请接口中，缺少必填参数refund_fee！");
                            //throw new WxPayException("退款申请接口中，缺少必填参数refund_fee！");
                        }
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—Refund", "退款申请接口中，缺少必填参数total_fee！");
                        //throw new WxPayException("退款申请接口中，缺少必填参数total_fee！");
                    }
                }
                else
                {
                    Log.Error("Wechat—V2—PayApiV2—Refund", "退款申请接口中，缺少必填参数out_refund_no！");
                    //throw new WxPayException("退款申请接口中，缺少必填参数out_refund_no！");
                }
            }
            else
            {
                Log.Error("Wechat—V2—PayApiV2—Refund", "退款申请接口中，out_trade_no、transaction_id至少填一个！");
                //throw new WxPayException("退款申请接口中，out_trade_no、transaction_id至少填一个！");
            }
            return result;
        }

        /// <summary>
        /// 查询退款
        /// 提交退款申请后，通过该接口查询退款状态。退款有一定延时，
        /// 用零钱支付的退款20分钟内到账，银行卡支付的退款3个工作日后重新查询退款状态。
        /// out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个
        /// </summary>
        /// <param name="out_refund_no">商户退款单号</param>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="transaction_id">微信支付订单号</param>
        /// <param name="refund_id">微信退款单号</param>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns></returns>
        public static ResponseRefundQueryResult RefundQuery(string out_refund_no, string out_trade_no, string transaction_id, string refund_id, string appid, string mchid, string apiKeyV2, int payMode = 0, string sub_appid = "", string sub_mchid = "", int timeOut = 6)
        {
            ResponseRefundQueryResult result = null;

            //请求参数
            RequestBase requestBase = new RequestBase();

            string url = DomainUtil.GetDomainName(Config.GetWechatPayUrl(), "/pay/refundquery");

            //检测必填参数
            if (!string.IsNullOrEmpty(out_refund_no) || !string.IsNullOrEmpty(out_trade_no) || !string.IsNullOrEmpty(transaction_id) || !string.IsNullOrEmpty(refund_id))
            {
                if (!string.IsNullOrEmpty(out_refund_no))
                {
                    requestBase.setParameter("out_refund_no", out_refund_no);
                }
                if (!string.IsNullOrEmpty(out_trade_no))
                {
                    requestBase.setParameter("out_trade_no", out_trade_no);
                }
                if (!string.IsNullOrEmpty(transaction_id))
                {
                    requestBase.setParameter("transaction_id", transaction_id);
                }
                if (!string.IsNullOrEmpty(refund_id))
                {
                    requestBase.setParameter("refund_id", refund_id);
                }

                requestBase.setParameter("appid", appid);//服务商的APPID
                requestBase.setParameter("mch_id", mchid);//商户号
                if (payMode == 1)
                {
                    #region 子商户关联APPID
                    if (!string.IsNullOrEmpty(sub_appid))
                    {
                        requestBase.setParameter("sub_appid", sub_appid);//子商户关联APPID
                    }
                    #endregion
                    #region 子商户号
                    if (!string.IsNullOrEmpty(sub_mchid))
                    {
                        requestBase.setParameter("sub_mch_id", sub_mchid);//子商户号
                    }
                    #endregion
                }

                requestBase.setParameter("nonce_str", StringUtil.GenerateNonceStr());//随机字符串
                requestBase.setParameter("sign_type", sign_type);//签名类型

                string sign = SignatureV2.MakeSign(requestBase.getAllParameters(), sign_type, apiKeyV2);//签名
                requestBase.setParameter("sign", sign);

                string xml = SignatureV2.ToXml(requestBase.getAllParameters());

                var start = DateTime.Now;
                Log.Debug("Wechat—V2—PayApiV2—RefundQuery", "RefundQuery request : " + xml);
                ResponseHandlerV2 responseHandler = new ResponseHandlerV2(url, mchid, "POST", xml, false, null, null, timeOut);
                var end = DateTime.Now;
                TimeSpan timespan = end.Subtract(start);//时间间隔
                Log.Debug("Wechat—V2—PayApiV2—RefundQuery", "RefundQuery 耗时 : " + timespan + " 秒");

                string responseBody = responseHandler.ResponseBody;
                Log.Debug("Wechat—V2—PayApiV2—RefundQuery", "RefundQuery response : " + responseBody);
                if (!string.IsNullOrEmpty(responseBody))
                {
                    var orderData = JsonUtil.JsonToT<ResponseRefundQueryResult>(responseBody);
                    string return_code = orderData.return_code;
                    if (return_code == "SUCCESS")
                    {
                        string result_code = orderData.result_code;
                        if (result_code == "SUCCESS")
                        {
                            var hashtable = SignatureV2.ObjectToHashtable(orderData);
                            //验证签名
                            if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                            {
                                result = orderData;
                            }
                            else
                            {
                                Log.Error("Wechat—V2—PayApiV2—RefundQuery", "签名验证错误！");
                            }
                        }
                        else
                        {
                            Log.Error("Wechat—V2—PayApiV2—RefundQuery", "返回结果result_code【" + result_code + "】！");
                        }
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—RefundQuery", "返回结果return_code【" + return_code + "】！");
                    }
                }
                else
                {
                    Log.Error("Wechat—V2—PayApiV2—RefundQuery", "申请退款失败！");
                }
            }
            else
            {
                Log.Error("Wechat—V2—PayApiV2—RefundQuery", "退款查询接口中，out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个！");
                //throw new WxPayException("退款查询接口中，out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个！");
            }
            return result;
        }

        /// <summary>
        /// 下载对账单
        /// </summary>
        /// <param name="bill_date">对账单日期</param>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns>成功时返回，其他抛异常</returns>
        public static string DownloadBill(string bill_date, string appid, string mchid, string apiKeyV2, int payMode = 0, string sub_appid = "", string sub_mchid = "", int timeOut = 6)
        {
            string result = null;

            //请求参数
            RequestBase requestBase = new RequestBase();

            string url = DomainUtil.GetDomainName(Config.GetWechatPayUrl(), "/pay/downloadbill");

            //检测必填参数
            if (!string.IsNullOrEmpty(bill_date))
            {
                requestBase.setParameter("bill_date", bill_date);
                requestBase.setParameter("appid", appid);//服务商的APPID
                requestBase.setParameter("mch_id", mchid);//商户号
                if (payMode == 1)
                {
                    #region 子商户关联APPID
                    if (!string.IsNullOrEmpty(sub_appid))
                    {
                        requestBase.setParameter("sub_appid", sub_appid);//子商户关联APPID
                    }
                    #endregion
                    #region 子商户号
                    if (!string.IsNullOrEmpty(sub_mchid))
                    {
                        requestBase.setParameter("sub_mch_id", sub_mchid);//子商户号
                    }
                    #endregion
                }

                requestBase.setParameter("nonce_str", StringUtil.GenerateNonceStr());//随机字符串
                requestBase.setParameter("sign_type", sign_type);//签名类型
                                                                 //签名
                string sign = SignatureV2.MakeSign(requestBase.getAllParameters(), sign_type, apiKeyV2);
                requestBase.setParameter("sign", sign);

                string xml = SignatureV2.ToXml(requestBase.getAllParameters());

                var start = DateTime.Now;
                Log.Debug("Wechat—V2—PayApiV2—DownloadBill", "DownloadBill request : " + xml);
                ResponseHandlerV2 responseHandler = new ResponseHandlerV2(url, mchid, "POST", xml, true, null, null, timeOut);
                var end = DateTime.Now;
                TimeSpan timespan = end.Subtract(start);//时间间隔
                Log.Debug("Wechat—V2—PayApiV2—DownloadBill", "DownloadBill 耗时 : " + timespan + " 秒");

                string responseBody = responseHandler.ResponseBody;
                Log.Debug("Wechat—V2—PayApiV2—DownloadBill", "DownloadBill response : " + responseBody);
                if (!string.IsNullOrEmpty(responseBody))
                {
                    //若接口调用失败会返回xml格式的结果 成功则返回非xml格式的数据
                    if (responseBody.Substring(0, 5) != "<xml>")
                    {
                        //接口调用成功时
                        result = responseBody;
                    }
                    else
                    {
                        //接口调用失败时
                        Log.Error("Wechat—V2—PayApiV2—DownloadBill", "接口调用失败会，" + responseBody);
                    }
                }
                else
                {
                    Log.Error("Wechat—V2—PayApiV2—DownloadBill", "下载对账单失败！");
                }
            }
            else
            {
                Log.Error("Wechat—V2—PayApiV2—DownloadBill", "对账单接口中，缺少必填参数bill_date！");
                //throw new WxPayException("对账单接口中，缺少必填参数bill_date！");
            }
            return result;
        }

        /// <summary>
        /// 关闭订单(小程序支付/JSAPI支付/Native支付/APP支付/H5支付)
        /// </summary>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns>成功时返回，其他抛异常</returns>
        public static ResponseOrderCloseResult CloseOrder(string out_trade_no, string appid, string mchid, string apiKeyV2, int payMode = 0, string sub_appid = "", string sub_mchid = "", int timeOut = 6)
        {
            //将xml格式的结果转换为对象以返回
            ResponseOrderCloseResult result = null;

            //请求参数
            RequestBase requestBase = new RequestBase();

            string url = DomainUtil.GetDomainName(Config.GetWechatPayUrl(), "/pay/closeorder");

            //检测必填参数
            if (!string.IsNullOrEmpty(out_trade_no))
            {
                requestBase.setParameter("out_trade_no", out_trade_no);
                requestBase.setParameter("appid", appid);//服务商的APPID
                requestBase.setParameter("mch_id", mchid);//商户号
                if (payMode == 1)
                {
                    #region 子商户关联APPID
                    if (!string.IsNullOrEmpty(sub_appid))
                    {
                        requestBase.setParameter("sub_appid", sub_appid);//子商户关联APPID
                    }
                    #endregion
                    #region 子商户号
                    if (!string.IsNullOrEmpty(sub_mchid))
                    {
                        requestBase.setParameter("sub_mch_id", sub_mchid);//子商户号
                    }
                    #endregion
                }

                requestBase.setParameter("nonce_str", StringUtil.GenerateNonceStr());//随机字符串
                requestBase.setParameter("sign_type", sign_type);//签名类型
                string sign = SignatureV2.MakeSign(requestBase.getAllParameters(), sign_type, apiKeyV2);//签名
                requestBase.setParameter("sign", sign);
                string xml = SignatureV2.ToXml(requestBase.getAllParameters());

                var start = DateTime.Now;
                Log.Debug("Wechat—V2—PayApiV2—CloseOrder", "CloseOrder request : " + xml);
                ResponseHandlerV2 responseHandler = new ResponseHandlerV2(url, mchid, "POST", xml, false, null, null, timeOut);
                var end = DateTime.Now;
                TimeSpan timespan = end.Subtract(start);//时间间隔
                Log.Debug("Wechat—V2—PayApiV2—CloseOrder", "CloseOrder 耗时 : " + timespan + " 秒");

                string responseBody = responseHandler.ResponseBody;
                Log.Debug("Wechat—V2—PayApiV2—CloseOrder", "CloseOrder response : " + responseBody);
                if (!string.IsNullOrEmpty(responseBody))
                {
                    var orderData = JsonUtil.JsonToT<ResponseOrderCloseResult>(responseBody);
                    string return_code = orderData.return_code;
                    if (return_code == "SUCCESS")
                    {
                        string result_code = orderData.result_code;
                        if (result_code == "SUCCESS")
                        {
                            var hashtable = SignatureV2.ObjectToHashtable(orderData);
                            //验证签名
                            if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                            {
                                result = orderData;
                            }
                            else
                            {
                                Log.Error("Wechat—V2—PayApiV2—CloseOrder", "签名验证错误！");
                            }
                        }
                        else
                        {
                            Log.Error("Wechat—V2—PayApiV2—CloseOrder", "返回结果result_code【" + result_code + "】！");
                        }
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—CloseOrder", "返回结果return_code【" + return_code + "】！");
                    }
                }
                else
                {
                    Log.Error("Wechat—V2—PayApiV2—CloseOrder", "关闭订单接口结果失败！");
                }
            }
            else
            {
                Log.Error("Wechat—V2—PayApiV2—CloseOrder", "关闭订单接口中，out_trade_no必填！");
            }
            return result;
        }

        /// <summary>
        /// 交易保障 测速上报接口实现
        /// </summary>
        /// <param name="interface_url">接口URL</param>
        /// <param name="timeCost">接口耗时</param>
        /// <param name="return_code">返回状态码</param>
        /// <param name="result_code">业务结果</param>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="return_msg">返回信息</param>
        /// <param name="err_code">错误代码</param>
        /// <param name="err_code_des">错误代码描述</param>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="device_info">设备号 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        public static void Report(string interface_url, int timeCost, string return_code, string result_code, string appid, string mchid, string apiKeyV2, int payMode = 0, string sub_mchid = "", string sub_appid = "", string return_msg = "", string err_code = "", string err_code_des = "", string out_trade_no = "", string device_info = "", int timeOut = 1)
        {
            #region 判断是否需要上报
            //默认允许上报
            bool isReport = true;

            var report_level = Convert.ToInt32(Config.GetValue("log_level"));
            //如果不需要进行上报
            if (report_level == 0)
            {
                isReport = false;
            }

            //如果仅失败上报
            if (report_level == 1)
            {
                if (!string.IsNullOrEmpty(return_code))
                {
                    //返回状态码
                    if (return_code == "SUCCESS")
                    {
                        if (!string.IsNullOrEmpty(result_code))
                        {
                            //业务结果
                            if (result_code == "SUCCESS")
                            {
                                isReport = false;
                            }
                        }
                    }
                }
            }
            #endregion

            //是否允许上报
            if (isReport)
            {
                //请求参数
                RequestBase requestBase = new RequestBase();

                string url = DomainUtil.GetDomainName(Config.GetWechatPayUrl(), "/payitil/report");

                //返回信息
                if (!string.IsNullOrEmpty(return_msg))
                {
                    requestBase.setParameter("return_msg", return_msg);
                }

                //错误代码
                if (!string.IsNullOrEmpty(err_code))
                {
                    requestBase.setParameter("err_code", err_code);
                }
                //错误代码描述
                if (!string.IsNullOrEmpty(err_code_des))
                {
                    requestBase.setParameter("err_code_des", err_code_des);
                }
                //商户订单号
                if (!string.IsNullOrEmpty(out_trade_no))
                {
                    requestBase.setParameter("out_trade_no", out_trade_no);
                }
                //设备号
                if (!string.IsNullOrEmpty(device_info))
                {
                    requestBase.setParameter("device_info", device_info);
                }

                //检测必填参数
                if (!string.IsNullOrEmpty(interface_url))
                {
                    requestBase.setParameter("interface_url", interface_url);
                    if (!string.IsNullOrEmpty(return_code))
                    {
                        requestBase.setParameter("return_code", return_code);
                        if (!string.IsNullOrEmpty(result_code))
                        {
                            requestBase.setParameter("result_code", result_code);
                            if (timeCost > 0)
                            {
                                requestBase.setParameter("result_code", result_code);
                                requestBase.setParameter("execute_time_", timeCost);

                                requestBase.setParameter("appid", appid);//服务商的APPID
                                requestBase.setParameter("mch_id", mchid);//商户号

                                if (payMode == 1)
                                {
                                    #region 子商户关联APPID
                                    if (!string.IsNullOrEmpty(sub_appid))
                                    {
                                        requestBase.setParameter("sub_appid", sub_appid);//子商户关联APPID
                                    }
                                    #endregion

                                    #region 子商户号
                                    if (!string.IsNullOrEmpty(sub_mchid))
                                    {
                                        requestBase.setParameter("sub_mch_id", sub_mchid);//子商户号
                                    }
                                    #endregion
                                }
                                requestBase.setParameter("time", DateTime.Now.ToString("yyyyMMddHHmmss"));//商户上报时间	
                                requestBase.setParameter("user_ip", IPUtil.GetAddressIP()); //访问接口IP

                                requestBase.setParameter("nonce_str", StringUtil.GenerateNonceStr());//随机字符串
                                requestBase.setParameter("sign_type", sign_type);//签名类型

                                string sign = SignatureV2.MakeSign(requestBase.getAllParameters(), sign_type, apiKeyV2);//签名
                                requestBase.setParameter("sign", sign);

                                string xml = SignatureV2.ToXml(requestBase.getAllParameters());


                                var start = DateTime.Now;
                                Log.Debug("Wechat—V2—PayApiV2—Report", "Report request : " + xml);
                                ResponseHandlerV2 responseHandler = new ResponseHandlerV2(url, mchid, "POST", xml, false, null, null, timeOut);
                                var end = DateTime.Now;
                                TimeSpan timespan = end.Subtract(start);//时间间隔
                                Log.Debug("Wechat—V2—PayApiV2—Report", "Report 耗时 : " + timespan + " 秒");

                                string responseBody = responseHandler.ResponseBody;
                                Log.Debug("Wechat—V2—PayApiV2—Report", "Report response : " + responseBody);
                                if (!string.IsNullOrEmpty(responseBody))
                                {
                                    var orderData = JsonUtil.JsonToT<ResponseReportResult>(responseBody);
                                    string return_code_result = orderData.return_code;
                                    if (return_code_result == "SUCCESS")
                                    {
                                        string result_code_result = orderData.result_code;
                                        if (result_code_result == "SUCCESS")
                                        {

                                        }
                                        else
                                        {
                                            Log.Error("Wechat—V2—PayApiV2—Report", "返回结果result_code【" + result_code + "】！");
                                        }
                                    }
                                    else
                                    {
                                        Log.Error("Wechat—V2—PayApiV2—Report", "返回结果return_code【" + return_code + "】！");
                                    }
                                }
                            }
                            else
                            {
                                Log.Error("Wechat—V2—PayApiV2—Report", "缺少必填参数execute_time_！");
                            }
                        }
                        else
                        {
                            Log.Error("Wechat—V2—PayApiV2—Report", "业务结果，缺少必填参数result_code！");
                        }
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—Report", "返回状态码，缺少必填参数return_code！");
                    }
                }
                else
                {
                    Log.Error("Wechat—V2—PayApiV2—Report", "接口URL，缺少必填参数interface_url！");
                }
            }
        }

        /// <summary>
        /// 转换短链接
        /// 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX)，
        /// 减小二维码数据量，提升扫描速度和精确度。
        /// </summary>
        /// <param name="long_url">提交给转换短连接API的参数</param>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="timeOut">接口超时时间</param>
        /// <returns></returns>
        public static ResponseShortUrlResult ShortUrl(string long_url, string appid, string mchid, string apiKeyV2, int payMode = 0, string sub_mchid = "", string sub_appid = "", int timeOut = 6)
        {
            ResponseShortUrlResult result = null;

            //请求参数
            RequestBase requestBase = new RequestBase();

            string url = DomainUtil.GetDomainName(Config.GetWechatPayUrl(), "/tools/shorturl");

            //检测必填参数
            if (!string.IsNullOrEmpty(long_url))
            {
                requestBase.setParameter("long_url", long_url);//URL链接
                requestBase.setParameter("appid", appid);//服务商的APPID
                requestBase.setParameter("mch_id", mchid);//商户号
                if (payMode == 1)
                {
                    #region 子商户关联APPID
                    if (!string.IsNullOrEmpty(sub_appid))
                    {
                        requestBase.setParameter("sub_appid", sub_appid);//子商户关联APPID
                    }
                    #endregion
                    #region 子商户号
                    if (!string.IsNullOrEmpty(sub_mchid))
                    {
                        requestBase.setParameter("sub_mch_id", sub_mchid);//子商户号
                    }
                    #endregion
                }

                requestBase.setParameter("nonce_str", StringUtil.GenerateNonceStr());//随机字符串
                requestBase.setParameter("sign_type", sign_type);//签名类型
                string sign = SignatureV2.MakeSign(requestBase.getAllParameters(), sign_type, apiKeyV2);//签名
                requestBase.setParameter("sign", sign);
                string xml = SignatureV2.ToXml(requestBase.getAllParameters());

                var start = DateTime.Now;
                Log.Debug("Wechat—V2—PayApiV2—ShortUrl", "ShortUrl request : " + xml);
                ResponseHandlerV2 responseHandler = new ResponseHandlerV2(url, mchid, "POST", xml, false, null, null, timeOut);
                var end = DateTime.Now;
                TimeSpan timespan = end.Subtract(start);//时间间隔
                Log.Debug("Wechat—V2—PayApiV2—ShortUrl", "ShortUrl 耗时 : " + timespan + " 秒");

                string responseBody = responseHandler.ResponseBody;
                Log.Debug("Wechat—V2—PayApiV2—ShortUrl", "ShortUrl response : " + responseBody);
                if (!string.IsNullOrEmpty(responseBody))
                {
                    var orderData = JsonUtil.JsonToT<ResponseShortUrlResult>(responseBody);
                    string return_code = orderData.return_code;
                    if (return_code == "SUCCESS")
                    {
                        string result_code = orderData.result_code;
                        if (result_code == "SUCCESS")
                        {
                            var hashtable = SignatureV2.ObjectToHashtable(orderData);
                            //验证签名
                            if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                            {
                                result = orderData;
                            }
                            else
                            {
                                Log.Error("Wechat—V2—PayApiV2—ShortUrl", "签名验证错误！");
                            }
                        }
                        else
                        {
                            Log.Error("Wechat—V2—PayApiV2—GetSignKey", "返回结果result_code【" + result_code + "】！");
                        }
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—GetSignKey", "返回结果return_code【" + return_code + "】！");
                    }
                }
                else
                {
                    Log.Error("Wechat—V2—PayApiV2—GetSignKey", "获取验签秘钥API失败！");
                }
            }
            else
            {
                Log.Error("Wechat—V2—PayApiV2—ShortUrl", "需要转换的URL，签名用原串，传输需URL encode！");
                //throw new WxPayException("需要转换的URL，签名用原串，传输需URL encode！");
            }
            return result;
        }

        /// <summary>
        /// 获取验签秘钥API
        /// 微信支付仿真测试系统
        /// 商户开发者只需将正式API的调用URL增加一层 xdc/apiv2sandbox 路径，即可对接到仿真系统。
        /// 例如，付款码支付URL：https://api.mch.weixin.qq.com/pay/micropay
        /// 变更为：https://api.mch.weixin.qq.com/xdc/apiv2sandbox/pay/micropay。
        /// </summary>
        /// <param name="mchid"></param>
        /// <param name="apiKeyV2"></param>
        /// <param name="timeOut">接口超时时间</param>
        /// <returns></returns>
        public static ResponseGetSignKeyResult GetSignKey(string mchid, string apiKeyV2, int timeOut = 6)
        {
            ResponseGetSignKeyResult result = null;

            //请求参数
            RequestBase requestBase = new RequestBase();

            string url = DomainUtil.GetDomainName(Config.GetWechatPayUrl(), "/sign/getsignkey");

            requestBase.setParameter("mch_id", mchid);//商户号

            requestBase.setParameter("nonce_str", StringUtil.GenerateNonceStr());//随机字符串
            requestBase.setParameter("sign_type", sign_type);//签名类型
            string sign = SignatureV2.MakeSign(requestBase.getAllParameters(), sign_type, apiKeyV2);//签名
            requestBase.setParameter("sign", sign);
            string xml = SignatureV2.ToXml(requestBase.getAllParameters());

            var start = DateTime.Now;
            Log.Debug("Wechat—V2—PayApiV2—CloseOrder", "CloseOrder request : " + xml);
            ResponseHandlerV2 responseHandler = new ResponseHandlerV2(url, mchid, "POST", xml, false, null, null, timeOut);
            var end = DateTime.Now;
            TimeSpan timespan = end.Subtract(start);//时间间隔
            Log.Debug("Wechat—V2—PayApiV2—CloseOrder", "CloseOrder 耗时 : " + timespan + " 秒");

            string responseBody = responseHandler.ResponseBody;
            Log.Debug("Wechat—V2—PayApiV2—CloseOrder", "CloseOrder response : " + responseBody);
            if (!string.IsNullOrEmpty(responseBody))
            {
                var orderData = JsonUtil.JsonToT<ResponseGetSignKeyResult>(responseBody);
                string return_code = orderData.return_code;
                if (return_code == "SUCCESS")
                {
                    string result_code = orderData.result_code;
                    if (result_code == "SUCCESS")
                    {
                        var hashtable = SignatureV2.ObjectToHashtable(orderData);
                        //验证签名
                        if (SignatureV2.CheckSign(hashtable, sign_type, apiKeyV2))
                        {
                            result = orderData;
                        }
                        else
                        {
                            Log.Error("Wechat—V2—PayApiV2—GetSignKey", "签名验证错误！");
                        }
                    }
                    else
                    {
                        Log.Error("Wechat—V2—PayApiV2—GetSignKey", "返回结果result_code【" + result_code + "】！");
                    }
                }
                else
                {
                    Log.Error("Wechat—V2—PayApiV2—GetSignKey", "返回结果return_code【" + return_code + "】！");
                }
            }
            else
            {
                Log.Error("Wechat—V2—PayApiV2—GetSignKey", "获取验签秘钥API失败！");
            }
            return result;
        }

        #endregion

        #region 业务方法

        /*
        如果开发者已做过JSAPI或JSSDK调起微信支付，接入小程序支付非常相似，以下是三种接入方式的对比：
        对比栏目            JSAPI	        JSSDK	        小程序
        统一下单	        都需要先获取到Openid，调用相同的API
        调起数据签名	    五个字段参与签名(区分大小写)：appId，nonceStr，package，signType，timeStamp
        调起支付页面协议    HTTP或HTTPS	    HTTP或HTTPS	    HTTPS
        支付目录	        有	            有	            无
        授权域名	        有	            有	            无
        回调函数	        有	            success回调	    complete、fail、success回调函数
        注意：小程序不能通过拉起H5页面做jsapi支付，小程序内只能使用小程序支付
        程序访问商户服务都是通过HTTPS,开发部署的时候需要安装HTTPS服务器
         */
        /// <summary>
        /// JSAPI/公众号/小程序调起支付所需的参数
        /// 从下单成功返回的数据中获取微信浏览器调起jsapi支付所需的参数
        /// 微信浏览器调起JSAPI时的输入参数格式如下：
        /// {
        ///   "appId" : "wx2421b1c4370ec43b",     //微信分配的小程序ID，服务商模式下应为当前调起支付小程序的appid    
        ///   "timeStamp":" 1395712654",         //时间戳，自1970年以来的秒数 
        ///   "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串 
        ///   "package" : "prepay_id=u802345jgfjsdfgsdg888", 
        ///   "signType" : "MD5",         //微信签名方式:    
        ///  "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
        /// }
        /// </summary>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="apiKeyV2">API密钥V2</param>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="total_fee">总金额</param>
        /// <param name="description">商品描述</param>
        /// <param name="notify_url">支付结果 通知地址</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="openid">用户标识</param>
        /// <param name="sub_openid">用户子标识</param>
        /// <param name="sub_appid">子商户对应应用APPID</param>
        /// <param name="sub_mchid">子商户号</param>
        /// <param name="product_id">商品ID</param>
        /// <param name="attach">附加数据</param>
        /// <param name="time_start">交易起始时间</param>
        /// <param name="time_expire">交易结束时间</param>
        /// <param name="goods_tag">订单优惠标记</param>
        /// <param name="scene_info">订单优惠标记</param>
        /// <param name="device_info">设备号 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"</param>
        /// <param name="detail">商品详情 商品详细描述，对于使用单品优惠的商户，该字段必须按照规范上传，详见“单品优惠参数说明”
        /// [{
        ///     "goods_detail":[
        ///     {
        ///         "goods_id":"iphone6s_16G",
        ///         "wxpay_goods_id":"1001",
        ///         "goods_name":"iPhone6s 16G",
        ///         "quantity":1,
        ///         "price":528800,
        ///         "goods_category":"123456",
        ///         "body":"苹果手机"
        ///     },
        ///     {
        ///         "goods_id":"iphone6s_32G",
        ///         "wxpay_goods_id":"1002",
        ///         "goods_name":"iPhone6s 32G",
        ///         "quantity":1,
        ///         "price":608800,
        ///         "goods_category":"123789",
        ///         "body":"苹果手机"
        ///     }
        ///     ]
        /// }]
        /// </param>
        /// <param name="fee_type">货币类型 符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型</param>
        /// <param name="limit_pay">指定支付方式  no_credit--指定不能使用信用卡支付</param>
        /// <param name="receipt">电子发票入口开放标识    Y，传入Y时，支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能，传此字段才可生效</param>
        /// <param name="profit_sharing">是否需要分账 Y-是，需要分账 N-否，不分账 字母要求大写，不传默认不分账</param>
        /// <param name="timeOut">超时时间，单位：秒</param>
        /// <returns></returns>
        public static string JsapiTuneUpPay(string appid, string mchid, string apiKeyV2, string out_trade_no, string total_fee, string description, string notify_url, int payMode = 0, string openid = "", string sub_openid = "", string sub_appid = "", string sub_mchid = "", string product_id = "", string attach = "", string time_start = "", string time_expire = "", string goods_tag = "", object scene_info = null, string device_info = "", object detail = null, string fee_type = "CNY", string limit_pay = "", string receipt = "", string profit_sharing = "N", int timeOut = 6)
        {
            var json = "";
            var result = JsApiPay(appid, mchid, apiKeyV2, out_trade_no, total_fee, description, notify_url, payMode, openid, sub_openid, sub_appid, sub_mchid, product_id, attach, time_start, time_expire, goods_tag, scene_info, device_info, detail, fee_type, limit_pay, receipt, profit_sharing, timeOut);
            if (result != null)
            {
                //商户申请的公众号对应的appid，由微信支付生成，可在公众号后台查看。若下单时传了sub_appid，可为sub_appid的值。
                //商户申请的小程序对应的appid，由微信支付生成，可在小程序后台查看。若下单时候传了sub_appid,须为sub_appid的值。
                //微信开放平台审核通过的移动应用appid ，为二级商户申请的应用appid。

                // 普通商户 默认返回的appid  
                // 服务商 使用子商户对用的appid 若下单时候传了sub_appid,须为sub_appid的值。
                if (payMode == 1)
                {
                    if (!string.IsNullOrEmpty(sub_appid))
                    {
                        appid = sub_appid;
                    }
                }

                RequestBase requestBase = new RequestBase();
                requestBase.setParameter("appId", appid);
                requestBase.setParameter("timeStamp", TimeUtil.GenerateTimeStamp());
                requestBase.setParameter("nonceStr", StringUtil.GenerateNonceStr());
                requestBase.setParameter("package", "prepay_id=" + result.prepay_id);
                requestBase.setParameter("signType", sign_type);

                string paySign = SignatureV2.MakeSign(requestBase.getAllParameters(), sign_type, apiKeyV2);
                requestBase.setParameter("paySign", paySign);

                json = JsonUtil.ObjectToJson(requestBase.getAllParameters());
                Log.Debug("Wechat—V2—PayApiV2—JsapiTuneUpPay", "Get jsApiParam : " + json);
            }
            else
            {
                Log.Error("Wechat—V2—PayApiV2—JsapiTuneUpPay", "统一下单未返回任何结果！");
            }

            return json;
        }

        /// <summary>
        /// APP调起支付所需的参数
        /// 在APP端调起支付的参数列表，注意：该支付参数的签名是服务商在后台生成的，用的是服务商的API密钥
        /// </summary>
        /// <param name="appid">（服务商）应用ID</param>
        /// <param name="mchid">（服务商）商户号</param>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="total_fee">订单总金额，单位为元。</param>
        /// <param name="description">商品描述</param>
        /// <param name="notify_url">支付结果 通知地址</param>
        /// <param name="payMode">默认为0。 0 普通商户 1 服务商</param>
        /// <param name="sub_mchid">子商户号  服务商必填</param>
        /// <param name="sub_appid">子商户应用ID 传入</param>
        /// <param name="attach">附加数据</param>
        /// <param name="time_expire">交易结束时间
        /// 订单失效时间，遵循rfc3339标准格式，格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE，yyyy-MM-DD表示年月日，T出现在字符串中，表示time元素的开头，HH:mm:ss表示时分秒，TIMEZONE表示时区（+08:00表示东八区时间，领先UTC8小时，即北京时间）。例如：2015-05-20T13:29:35+08:00表示，北京时间2015年5月20日 13点29分35秒。
        /// 订单失效时间是针对订单号而言的，由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期，所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id。其他详见时间规则 。
        /// 建议：最短失效时间间隔大于1分钟
        /// </param>
        /// <param name="goods_tag">订单优惠标记</param>
        /// <param name="scene_info">场景信息</param>
        /// <param name="detail">优惠功能</param>
        /// <param name="settle_info">结算信息</param>
        /// <returns></returns>
        public static string AppTuneUpPay(string appid, string mchid, string apiKeyV2, string out_trade_no, string total_fee, string description, string notify_url, int payMode = 0, string sub_appid = "", string sub_mchid = "", string attach = "", string time_start = "", string time_expire = "", string goods_tag = "", object scene_info = null, string device_info = "", object detail = null, string fee_type = "CNY", string limit_pay = "", string receipt = "", string profit_sharing = "N", int timeOut = 6)
        {
            string result = "";
            var jsApiResult = AppPay(appid, mchid, apiKeyV2, out_trade_no, total_fee, description, notify_url, payMode, sub_appid, sub_mchid, attach, time_start, time_expire, goods_tag, scene_info, device_info, detail, fee_type, limit_pay, receipt, profit_sharing, timeOut);
            if (jsApiResult != null)
            {
                // 普通商户 默认返回的appid  
                // 服务商 服务商申请的公众号或移动应用appid。若下单时传了sub_appid，可为sub_appid的值。
                if (payMode == 1)
                {
                    if (!string.IsNullOrEmpty(sub_appid))
                    {
                        appid = sub_appid;
                    }
                }
                string timeStamp = TimeUtil.GenerateTimeStamp();
                string nonceStr = StringUtil.GenerateNonceStr();
                string prepayid = jsApiResult.prepay_id;

                RequestBase requestBase = new RequestBase();
                requestBase.setParameter("appId", appid); //服务商申请的公众号或移动应用appid。若下单时传了sub_appid，可为sub_appid的值。
                requestBase.setParameter("partnerId", mchid); //商户号 请填写商户号mchid对应的值。
                requestBase.setParameter("prepayId", prepayid); //预支付交易会话ID 请填写商户号mchid对应的值。
                requestBase.setParameter("package", "Sign=WXPay"); //订单详情扩展字符串 暂填写固定值Sign=WXPay
                requestBase.setParameter("timeStamp", timeStamp);
                requestBase.setParameter("nonceStr", nonceStr);

                //签名，详见签名生成算法注意：签名方式一定要与统一下单接口使用的一致
                var hashtable = requestBase.getAllParameters();
                string paySign = SignatureV2.MakeSign(hashtable, sign_type, apiKeyV2);  //签名
                requestBase.setParameter("sign", paySign);

                result = JsonUtil.ObjectToJson(requestBase.getAllParameters());
            }
            else
            {
                Log.Error("Wechat—V2—PayApiV2—AppTuneUpPay", "统一下单未返回任何结果！");
            }
            return result;
        }

        #endregion
    }
}
